package com.alks.function.service.impl.system;

import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.service.RedisService;
import com.alks.common.utils.MinIoUtil;
import com.alks.common.utils.beanutils.StringUtils;
import com.alks.common.utils.export.dto.FileInfoDTO;
import com.alks.common.utils.export.constants.FileStatusEnum;
import com.alks.common.utils.export.service.IExportRecordService;
import com.alks.common.utils.export.service.util.ExportTaskUtil;
import com.alks.entity.data.entity.sys.SysFileExportRecord;
import com.alks.entity.exception.FileException;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.request.sys.FileExportRecordRequest;
import com.alks.function.data.vo.stock.PageDataVO;
import com.alks.function.data.vo.sys.FileExportRecordVO;
import com.alks.function.data.vo.sys.FileGenerateInfoVO;
import com.alks.function.mapper.system.SysFileExportRecordMapper;
import com.alks.function.service.system.ISysFileExportRecordService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.pagehelper.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: chen
 * @date: 2024/12/27
 * @description: 文件导出记录
 */
@Slf4j
@Service
public class SysFileExportRecordImpl implements ISysFileExportRecordService, IExportRecordService {

    @Resource
    private SysFileExportRecordMapper sysFileExportRecordMapper;
    @Resource
    private MinIoUtil minIoUtil;
    @Resource
    private RedisService redisService;

    @Override
    @AutoPageAop
    public PageDataVO<FileExportRecordVO> termPage(FileExportRecordRequest request) {
        PageDataVO<FileExportRecordVO> res = new PageDataVO<>();
        LambdaQueryWrapper<SysFileExportRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(!StringUtils.isEmpty(request.getOriginal()), SysFileExportRecord::getOriginal, request.getOriginal());
        queryWrapper.eq(request.getStatus() != null, SysFileExportRecord::getStatus, request.getStatus());
        queryWrapper.eq(SysFileExportRecord::getCreateBy, UserIdThread.get().getUserId());
        queryWrapper.eq(SysFileExportRecord::getDelFlag, 0);
        queryWrapper.orderByDesc(SysFileExportRecord::getCreateTime);
        queryWrapper.orderByDesc(SysFileExportRecord::getGenerationTime);
        List<SysFileExportRecord> list = sysFileExportRecordMapper.selectList(queryWrapper);
        Page<SysFileExportRecord> page = (Page<SysFileExportRecord>) list;

        List<FileExportRecordVO> recordVOS = page.getResult().stream().map(FileExportRecordVO::toVO).collect(Collectors.toList());
        res.setRows(recordVOS);
        res.setTotal(page.getTotal());
        return res;
    }

    @Override
    public List<FileExportRecordVO> getRecordList() {
        LambdaQueryWrapper<SysFileExportRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysFileExportRecord::getStatus, FileStatusEnum.PREPARING.getCode());
        queryWrapper.eq(SysFileExportRecord::getCreateBy, UserIdThread.get().getUserId());
        queryWrapper.eq(SysFileExportRecord::getDelFlag, 0);
        queryWrapper.orderByDesc(SysFileExportRecord::getCreateTime);
        List<SysFileExportRecord> list = sysFileExportRecordMapper.selectList(queryWrapper);
        return list.stream().map(FileExportRecordVO::toVO).collect(Collectors.toList());
    }

    @Override
    public FileGenerateInfoVO checkStatus(String userId) {
        String FILE_GENERATION_CACHE = "file:generate:%s";
        String key = String.format(FILE_GENERATION_CACHE, userId);
        Long generateNum = null;
        String cacheVal = redisService.get(key);
        if (!StringUtils.isEmpty(cacheVal)) {
            generateNum = Long.parseLong(cacheVal);
        }
        long currentNum = ExportTaskUtil.existsExportTask(userId);

        FileGenerateInfoVO infoVO = new FileGenerateInfoVO();
        if (currentNum > 0) {
            if (generateNum == null || currentNum != generateNum) {
                infoVO.setChange(true);
                redisService.set(key, String.valueOf(currentNum));
            }
        } else {
            infoVO.setClose(true);
            if (generateNum != null && currentNum < generateNum) {
                infoVO.setChange(true);
                redisService.delete(key);
            }
        }
        return infoVO;
    }

    @Override
    public boolean record(FileInfoDTO fileInfoDTO) {
        SysFileExportRecord record = new SysFileExportRecord();
        record.setCreateBy(fileInfoDTO.getCreateBy());
        record.setBucket(fileInfoDTO.getBucket());
        record.setFileName(fileInfoDTO.getFileName());
        record.setOriginal(fileInfoDTO.getOriginal());
        record.setCreateTime(fileInfoDTO.getCreateTime());
        record.setStatus(FileStatusEnum.PREPARING.getCode());
        return sysFileExportRecordMapper.insert(record) > 0;
    }

    @Override
    public boolean updateRecord(FileInfoDTO fileInfoDTO, Boolean success) {
        LambdaQueryWrapper<SysFileExportRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysFileExportRecord::getCreateBy, fileInfoDTO.getCreateBy());
        queryWrapper.eq(SysFileExportRecord::getFileName, fileInfoDTO.getFileName());
        queryWrapper.eq(SysFileExportRecord::getStatus, FileStatusEnum.PREPARING.getCode());
        List<SysFileExportRecord> records = sysFileExportRecordMapper.selectList(queryWrapper);
        if (records.isEmpty()) {
            return false;
        }
        SysFileExportRecord record = new SysFileExportRecord();
        record.setId(records.get(0).getId());
        if (success) {
            record.setFileSize(fileInfoDTO.getFileSize());
            record.setGenerationTime(fileInfoDTO.getGenerationTime());
            record.setStatus(FileStatusEnum.READY.getCode());
        } else {
            record.setStatus(FileStatusEnum.ERROR.getCode());
        }
        return sysFileExportRecordMapper.updateById(record) > 0;
    }

    @Override
    public void download(String bucket, String fileName, HttpServletResponse response) {
        LambdaQueryWrapper<SysFileExportRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysFileExportRecord::getBucket, bucket);
        queryWrapper.eq(SysFileExportRecord::getFileName, fileName);
        queryWrapper.eq(SysFileExportRecord::getDelFlag, 0);
        queryWrapper.notIn(SysFileExportRecord::getStatus, FileStatusEnum.PREPARING.getCode(), FileStatusEnum.FILE_EXPIRE.getCode());
        List<SysFileExportRecord> records = sysFileExportRecordMapper.selectList(queryWrapper);
        if (records.isEmpty()) {
            throw new ServiceErrorException("文件不存在、未生成或已失效！");
        }
        boolean update = true;
        Integer status = FileStatusEnum.DOWNLOADED.getCode();
        try {
            minIoUtil.downloadRename(bucket, fileName, records.get(0).getOriginal(), response);
        } catch (FileException ex) {
            status = FileStatusEnum.FILE_EXPIRE.getCode();
            throw ex;
        } catch (Exception ex) {
            update = false;
            throw ex;
        } finally {
            if (update) {
                LambdaUpdateWrapper<SysFileExportRecord> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(SysFileExportRecord::getId, records.get(0).getId());
                updateWrapper.set(SysFileExportRecord::getStatus, status);
                updateWrapper.set(SysFileExportRecord::getDownloadTime, LocalDateTime.now());
                sysFileExportRecordMapper.update(null, updateWrapper);
            }
        }
    }

    @Override
    public Boolean removeRecord(Long id) {
        LambdaUpdateWrapper<SysFileExportRecord> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(SysFileExportRecord::getId, id);
        updateWrapper.set(SysFileExportRecord::getDelFlag, 1);
        return sysFileExportRecordMapper.update(null, updateWrapper) > 0;
    }
}
